package by.bsu.diploma.system.factory.impl;

import by.bsu.diploma.common.Grid;
import by.bsu.diploma.common.Orientation;
import by.bsu.diploma.common.Word;
import by.bsu.diploma.system.Configuration;
import by.bsu.diploma.system.factory.CrosswordFactory;

import java.util.*;

import static by.bsu.diploma.common.Constants.*;

/**
 * @author Anatoly Selitsky
 */
public class ClassicCrosswordFactory implements CrosswordFactory {

    private Grid grid;
    private int quantity = 0;

    @Override
    public void generate(Configuration configuration) {
        if (configuration.getWords() == null) {
            throw new IllegalArgumentException("List words can not be empty");
        }
        grid = new Grid(configuration.getRowCount(), configuration.getColCount());
        System.out.println(grid);
        Map<Integer, Integer> history = new HashMap<Integer, Integer>();
        try {
            populate(configuration.getWords(), history);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        System.out.println(grid);
    }

    public void populate(List<Word> words, Map<Integer, Integer> history) {
        Collections.sort(words);
        Random random = new Random();
        int row, col, length;
        for (Word word : words) {
            length = word.getLength();
            for (int i = 0; i < word.getCount(); i++) {
                if (history.containsKey(length)) {
                    if (history.get(length) == word.getCount()) {
                        break;
                    }
                }
                row = random.nextInt(grid.getRowCount());
                col = random.nextInt(grid.getColumnCount());
                if (row + length - 1 < grid.getRowCount()) {
                    if (isPopulate(row, col, length, Orientation.VERTICAL)) {
                        updateHistory(history, word);
                    } else {
                        populate(words, history);
                    }
                } else if (row - length + 1 >= 0) {
                    if (isPopulate(row - length + 1, col, length, Orientation.VERTICAL)) {
                        updateHistory(history, word);
                    } else {
                        populate(words, history);
                    }
                } else if (col + length - 1 < grid.getColumnCount()) {
                    if (isPopulate(row, col, length, Orientation.HORIZONTAL)) {
                        updateHistory(history, word);
                    } else {
                        populate(words, history);
                    }
                } else if (col - length + 1 >= 0) {
                    if (isPopulate(row, col - length + 1, length, Orientation.HORIZONTAL)) {
                        updateHistory(history, word);
                    } else {
                        populate(words, history);
                    }
                } else {
                    populate(words, history);
                }
            }
        }
    }

    public boolean isPopulate(int row, int col, int length, Orientation orientation) {
        if (quantity == 0) {
            addWordOnGrid(row, col, length, orientation);
            return true;
        } else if (noOverlappingWords(row, col, length, orientation) && hasIntersection(row, col, length, orientation)) {
            addWordOnGrid(row, col, length, orientation);
            return true;
        }
        return false;
    }

    public void addWordOnGrid(int row, int col, int length, Orientation orientation) {
        int cells[][] = grid.getCells();
        if (Orientation.VERTICAL.equals(orientation)) {
            for (int i = row; i < row + length; i++) {
                if (cells[i][col] == FILLED || cells[i][col] == INTERSECTION) {
                    cells[i][col] = INTERSECTION;
                } else {
                    cells[i][col] = FILLED;
                }
            }
        } else {
            for (int i = col; i < col + length; i++) {
                if (cells[row][i] == INTERSECTION || cells[row][i] == FILLED) {
                    cells[row][i] = INTERSECTION;
                } else {
                    cells[row][i] = FILLED;
                }
            }
        }
        quantity++;
    }

    public boolean noOverlappingWords(int row, int col, int length, Orientation orientation) {
        int countNoZero = 0, countZero = 0,
                cells[][] = grid.getCells();
        if (Orientation.VERTICAL.equals(orientation)) {
            for (int i = row; i < row + length; i++) {
                if (cells[i][col] == FILLED || cells[i][col] == INTERSECTION) {
                    countNoZero++;
                } else {
                    countZero++;
                }
            }
        } else {
            for (int i = col; i < col + length; i++) {
                if (cells[row][i] == FILLED || cells[row][i] == INTERSECTION) {
                    countNoZero++;
                } else {
                    countZero++;
                }
            }
        }
        return countNoZero != length; // && (countZero + countNoZero == length);
    }

    public boolean hasIntersection(int row, int col, int length, Orientation orientation) {
        int[][] cells = grid.getCells();
        boolean result = false;
        if (Orientation.VERTICAL.equals(orientation)) {
            for (int i = row; i < row + length; i++) {
                if (cells[i][col] == FILLED) {
                    if (!canPlaced(i, col)) {
                        return false;
                    }
                    result = true;
                }
            }
        } else {
            for (int j = col; j < col + length; j++) {
                if (cells[row][j] == FILLED) {
                    if (!canPlaced(row, j)) {
                        return false;
                    }
                    result = true;
                }
            }
        }
        return result;
    }

    public boolean canPlaced(int row, int col) {
        int rowCount = grid.getRowCount(),
                colCount = grid.getColumnCount(),
                cells[][] = grid.getCells();
        boolean result = false;
        if (row == 0) {
            if (col == 0) {
                result = cells[row + 1][col + 1] == EMPTY;
            } else if (col == colCount - 1) {
                result = cells[row + 1][col - 1] == EMPTY;
            } else {
                result = cells[row + 1][col - 1] == EMPTY && cells[row + 1][col + 1] == EMPTY;
            }
        } else if (row == rowCount - 1) {
            if (col == 0) {
                result = cells[row - 1][col + 1] == EMPTY;
            } else if (col == colCount - 1) {
                result = cells[row - 1][col - 1] == EMPTY;
            } else {
                result = cells[row - 1][col - 1] == EMPTY && cells[row - 1][col + 1] == EMPTY;
            }
        } else if (col == 0) {
            result = cells[row + 1][col + 1] == EMPTY && cells[row - 1][col + 1] == EMPTY;
        } else if (col == colCount - 1) {
            result = cells[row + 1][col - 1] == EMPTY && cells[row - 1][col - 1] == EMPTY;
        } else {
            result = cells[row - 1][col - 1] == EMPTY &&
                     cells[row + 1][col - 1] == EMPTY &&
                     cells[row + 1][col + 1] == EMPTY &&
                     cells[row - 1][col + 1] == EMPTY;
        }
        return result;
    }

    public void updateHistory(Map<Integer, Integer> history, Word word) {
        Integer key = word.getLength();
        if (history.containsKey(key)) {
            history.put(key, history.get(key) + 1);
        } else {
            history.put(key, 1);
        }
    }

}
